module spt_core(
    input          clk_100m           ,
    input          rst_spt_n          ,

    output         spt_cpuif_head_err ,
    output         spt_cpuif_tail_err ,
    output         spt_cpuif_short_pkt,
    output         spt_cpuif_long_pkt,
    output         spt_cpuif_ok_pkt,

    input          vid_in,
    input [15:0]   data_in,
    output reg     vid_out,
    output [15:0]  data_out,

    output         ram_w_en,
    output [15:0]  ram_w_data,
    output [10:0]  ram_w_addr,

    output         ram_r_en,
    input  [15:0]  ram_r_data,
    output [10:0]  ram_r_addr
);

// --------------------------------------------------------------------
// signal declare
// --------------------------------------------------------------------
reg vid_in_ff1, vid_in_ff2, vid_in_ff3, vid_in_ff4, vid_in_ff5;
reg [15:0]data_in_ff1, data_in_ff2, data_in_ff3, data_in_ff4, data_in_ff5;
wire vid_data_head_en;

wire data_head_err, data_tail_err, data_lng_err, data_sht_err;

reg [9:0]data_cnt;

reg     spt_cpuif_head_err_100m; 
reg     spt_cpuif_tail_err_100m; 
reg     spt_cpuif_short_pkt_100m;
reg     spt_cpuif_long_pkt_100m;
reg     spt_cpuif_ok_pkt_100m;


// --------------------------------------------------------------------
// status
// --------------------------------------------------------------------
localparam S_IDLE    = 4'd0;
localparam S_HEAD    = 4'd1;
localparam S_PAYLOAD = 4'd2;
localparam S_TAIL    = 4'd3;
localparam S_ERROR   = 4'd4;

reg [3:0]status, nx_status;
always @(posedge clk_100m or negedge rst_spt_n)begin
    if(!rst_spt_n) status <= S_IDLE;
    else           status <= nx_status;
end

always @* begin
    case(status)
        S_IDLE: begin
            if(vid_data_head_en)begin
                nx_status = S_HEAD;
            end
            else begin
                nx_status = S_IDLE;
            end
        end
        S_HEAD: begin
            if(data_head_err)begin
                nx_status = S_ERROR;
            end
            else begin
                nx_status = S_PAYLOAD;
            end
        end
        S_PAYLOAD: begin
            if(data_sht_err || data_lng_err)begin
                nx_status = S_ERROR;
            end
            else if(vid_in_ff3 == 1'b0 && vid_in_ff4 == 1'b1)begin
                nx_status = S_TAIL;
            end
            else begin
                nx_status = S_PAYLOAD;
            end
        end
        S_TAIL: begin
            nx_status = S_IDLE;
        end
        S_ERROR: begin
            if(vid_in_ff4 == 1'b0 && vid_in_ff5 == 1'b1)begin
                nx_status = S_IDLE;
            end
            else begin
                nx_status = S_ERROR;
            end
        end
        default: nx_status = S_IDLE;
    endcase
end

// --------------------------------------------------------------------
// data in 
// --------------------------------------------------------------------
assign vid_data_head_en = !vid_in_ff5 && vid_in_ff4 && vid_in_ff3 && vid_in_ff2;

always @(posedge clk_100m or negedge rst_spt_n)begin
    if(!rst_spt_n)begin
        vid_in_ff1 <= 1'b0;
        vid_in_ff2 <= 1'b0;
        vid_in_ff3 <= 1'b0;
        vid_in_ff4 <= 1'b0;
        vid_in_ff5 <= 1'b0;
    end
    else begin
        vid_in_ff1 <= vid_in;
        vid_in_ff2 <= vid_in_ff1;
        vid_in_ff3 <= vid_in_ff2;
        vid_in_ff4 <= vid_in_ff3;
        vid_in_ff5 <= vid_in_ff4;
    end
end

always @(posedge clk_100m)begin
    data_in_ff1 <= data_in;
    data_in_ff2 <= data_in_ff1;
    data_in_ff3 <= data_in_ff2;
    data_in_ff4 <= data_in_ff3;
    data_in_ff5 <= data_in_ff4;
end

always @(posedge clk_100m or negedge rst_spt_n)begin
    if(!rst_spt_n)begin
        data_cnt <= 10'b0;
    end
    else if(status == S_HEAD)begin
        data_cnt <= 10'b0;
    end
    else if(status == S_PAYLOAD)begin
        data_cnt <= data_cnt + 1'b1;
    end
end

// --------------------------------------------------------------------
// sram in
// --------------------------------------------------------------------
reg [10:0]data_in_addr_base;
always @(posedge clk_100m or negedge rst_spt_n)begin
    if(!rst_spt_n)begin
        data_in_addr_base <= 11'b0;
    end
    else if(status == S_TAIL && !data_tail_err && !data_sht_err && !data_lng_err)begin//may in tail checkout data_sht_err and data_lng_err
        data_in_addr_base <= data_in_addr_base + {1'b0, data_cnt};
    end
end

assign ram_w_en   = (status == S_PAYLOAD);
assign ram_w_addr = (data_in_addr_base + {1'b0, data_cnt});
assign ram_w_data = {data_in_ff5[7:0], data_in_ff5[15:8]};

// --------------------------------------------------------------------
// sram out
// --------------------------------------------------------------------
reg [10:0]data_out_addr;
always @(posedge clk_100m or negedge rst_spt_n)begin
    if(!rst_spt_n)begin
        data_out_addr <= 11'b0;
    end
    else if(ram_r_en)begin
        data_out_addr <= data_out_addr + 1'b1;
    end
end

assign ram_r_en   = (data_out_addr[10] == data_in_addr_base[10] && data_out_addr < data_in_addr_base) || (data_out_addr[10] != data_in_addr_base[10]);
assign ram_r_addr = data_out_addr;

always @(posedge clk_100m or negedge rst_spt_n)begin
    if(!rst_spt_n)begin
        vid_out <= 1'b0;
    end
    else begin
        vid_out <= ram_r_en;
    end
end

assign data_out = ram_r_data;

// --------------------------------------------------------------------
// irq
// --------------------------------------------------------------------
//head err
assign data_head_err = (status == S_HEAD && data_in_ff5 != 16'h55d5);
always @(posedge clk_100m or negedge rst_spt_n)begin
    if(!rst_spt_n)begin
        spt_cpuif_head_err_100m <= 1'b0;
    end
    else if(data_head_err)begin
        spt_cpuif_head_err_100m <= 1'b1;
    end
    else if(spt_cpuif_head_err_100m)begin
        spt_cpuif_head_err_100m <= 1'b0;
    end
end

//tail err
reg  [15:0]data_sum_q;
wire [16:0]data_sum_2byte = {1'b0, data_in_ff5} + {1'b0, data_sum_q};
wire [15:0]data_sum_d     = data_sum_2byte[16] + data_sum_2byte[15:0];
wire [15:0]data_sum       = (data_sum_q == 16'hffff) ? data_sum_q : ~data_sum_q;

always @(posedge clk_100m or negedge rst_spt_n)begin
    if(!rst_spt_n)begin
        data_sum_q <= 16'b0;
    end
    else if(status == S_IDLE)begin
        data_sum_q <= 16'b0;
    end
    else if(status == S_PAYLOAD)begin
        data_sum_q <= data_sum_d;
    end
end

assign data_tail_err = (status == S_TAIL && data_in_ff5 != data_sum && !data_sht_err && !data_lng_err);
always @(posedge clk_100m or negedge rst_spt_n)begin
    if(!rst_spt_n)begin
        spt_cpuif_tail_err_100m <= 1'b0;
    end
    else if(data_tail_err)begin
        spt_cpuif_tail_err_100m <= 1'b1;
    end
    else if(spt_cpuif_tail_err_100m)begin
        spt_cpuif_tail_err_100m <= 1'b0;
    end
end

//long pkt
assign data_lng_err = (data_cnt == 10'd601) && (status == S_PAYLOAD || status == S_TAIL);
always @(posedge clk_100m or negedge rst_spt_n)begin
    if(!rst_spt_n)begin
        spt_cpuif_long_pkt_100m <= 1'b0;
    end
    else if(data_lng_err)begin
        spt_cpuif_long_pkt_100m <= 1'b1;
    end
    else if(spt_cpuif_long_pkt_100m)begin
        spt_cpuif_long_pkt_100m <= 1'b0;
    end
end

//short pkt
assign data_sht_err = (status == S_TAIL && data_cnt < 10'd20);
always @(posedge clk_100m or negedge rst_spt_n)begin
    if(!rst_spt_n)begin
        spt_cpuif_short_pkt_100m <= 1'b0;
    end
    else if(data_sht_err)begin
        spt_cpuif_short_pkt_100m <= 1'b1;
    end
    else if(spt_cpuif_short_pkt_100m)begin
        spt_cpuif_short_pkt_100m <= 1'b0;
    end
end

//ok pkt
always @(posedge clk_100m or negedge rst_spt_n)begin
    if(!rst_spt_n)begin
        spt_cpuif_ok_pkt_100m <= 1'b0;
    end
    else if(status == S_TAIL && data_in_ff5 == data_sum && data_cnt >= 10'd20 && data_cnt <= 10'd600)begin
        spt_cpuif_ok_pkt_100m <= 1'b1;
    end
    else if(spt_cpuif_ok_pkt_100m)begin
        spt_cpuif_ok_pkt_100m <= 1'b0;
    end
end

bit_widen u_spt_cpuif_head_err_widen(.clk(clk_100m), .rst_n(rst_spt_n), .s_in(spt_cpuif_head_err_100m), .s_out(spt_cpuif_head_err));
bit_widen u_spt_cpuif_tail_err_widen(.clk(clk_100m), .rst_n(rst_spt_n), .s_in(spt_cpuif_tail_err_100m), .s_out(spt_cpuif_tail_err));
bit_widen u_spt_cpuif_short_pkt_widen(.clk(clk_100m), .rst_n(rst_spt_n), .s_in(spt_cpuif_short_pkt_100m), .s_out(spt_cpuif_short_pkt));
bit_widen u_spt_cpuif_long_pkt_widen(.clk(clk_100m), .rst_n(rst_spt_n), .s_in(spt_cpuif_long_pkt_100m), .s_out(spt_cpuif_long_pkt));
bit_widen u_spt_cpuif_ok_pkt_widen(.clk(clk_100m), .rst_n(rst_spt_n), .s_in(spt_cpuif_ok_pkt_100m), .s_out(spt_cpuif_ok_pkt));

endmodule
